#pragma once

#include <ddblk/locks/nlock_int.h>

typedef struct _clhnode
{
	struct _clhnode *pred;
	lkword_t enabled;
} _clhnode_t;

typedef struct _clhlock
{
	_clhnode_t *tail;
} _clhlock_t;

DECL_NLOCK(clh)

/* 解除节点分配器耦合 */
SAI void __clh_init(_clhlock_t *s, _clhnode_t *sentry)
{
	s->tail = sentry;
	sentry->enabled = TRUE;
	sentry->pred = NULL;
}
SAI void *__clh_destruct(_clhlock_t *s, _clhnode_t **psentry)
{
	ASSERT(s->tail != NULL);	/* 肯定的 */
	*psentry = s->tail;
	return (void *)s;
}
SAI _clhnode_t *__clh_lock(_clhlock_t *s, 
	INFUN(nlock_wait, waitpolicy), _clhnode_t *node, const acq_ord_t order){
	waitcnt_t cnt = -1;
	lkword_t node_disabled = 0;
	// ASSERT(node->enabled == FALSE);
	_clhnode_t *pred = swp_rel(&s->tail, node);
	atm_st(&node->pred, pred);

	while (!atm_ld(&pred->enabled))
	{
		cnt = waitpolicy->waitf(&node->enabled, &node_disabled, cnt);
		/* 不能返回FALSE, 因为有可能会死锁? */
		// if (! cnt) return FALSE;
	}

	if (order == LACQ) mp_rmb();
	// pred->enabled = FALSE;
	return pred;
}
SAI _clhnode_t *__clh_unlock(_clhlock_t *s,
	INFUN(nlock_wait, waitpolicy), _clhnode_t *node, const rel_ord_t order)
{
	PARAM_USED(s);
	lkword_t node_enabled = 1;
	_clhnode_t *pred = node->pred;
	if (order == RREL) atm_strel(&node->enabled, TRUE);
	else atm_st(&node->enabled, TRUE);

	/* 通知等待线程 */
	waitpolicy->wakef(&node->enabled, &node_enabled);
	return pred;
}

/* 添加分配器依赖 */
PARAM_NONULL(1) SAI _clhlock_t *_clh_init(void *ptr, size_t size, INFUN(nlock_lknode, lknode))
{
	if (size < sizeof(_clhlock_t)) return NULL;
	_clhnode_t *sentry = (_clhnode_t *)lknode->get_lknode(sizeof(_clhnode_t));
	_clhlock_t *s = (_clhlock_t *)ptr;
	__clh_init(s, sentry);
	return s;
}

PARAM_NONULL(1) SAI void *_clh_destruct(_clhlock_t *s, INFUN(nlock_lknode, lknode))
{
	_clhnode_t *sentry;
	void *ret = __clh_destruct(s, &sentry);
	lknode->ret_lknode(sentry, sizeof(_clhnode_t));
	return ret;
}

PARAM_NONULL(1) SAI bool_t _clh_lock(_clhlock_t *s, locktoken_t *ptoken, waitcnt_t cnt, 
	INFUN(nlock_wait, waitpolicy), INFUN(nlock_lknode, lknode), const acq_ord_t order)
{
	PARAM_USED(cnt);
	_clhnode_t *node = lknode->get_lknode(sizeof(_clhnode_t));
	*ptoken = node;
	/* 确保使用前enable为空 */
	node->enabled = FALSE;
	node->pred = NULL;
	__clh_lock(s, waitpolicy, node, order);
	
	return TRUE;
}

PARAM_NONULL(1, 2) SAI bool_t _clh_unlock(_clhlock_t *s, locktoken_t token, waitcnt_t cnt, 
	INFUN(nlock_wait, waitpolicy),  INFUN(nlock_lknode, lknode), const rel_ord_t order)
{
	PARAM_USED(s, cnt);
	_clhnode_t *node = (_clhnode_t *)token;
	_clhnode_t *pred = __clh_unlock(s, waitpolicy, node, order);
	/* 归还节点 */
	lknode->ret_lknode(pred, sizeof(_clhnode_t));
	return TRUE;
}

